/**
 * 
 */
package it.polimi.leo.jg;

import it.polimi.leo.mw.IMiddleWareGroup;
import it.polimi.leo.mw.IMiddleWareMsgHandler;
import it.polimi.leo.mw.IMiddleWareNode;
import it.polimi.leo.mw.IMiddlewareMessage;
import it.polimi.leo.mw.JoinGroupException;
import it.polimi.leo.mw.IMiddleWare.IMiddlewareMessageKind;
import java.io.Serializable;

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.logging.Level;
import java.util.logging.Logger;

import org.jgroups.Address;
import org.jgroups.JChannel;
import org.jgroups.Message;
import org.jgroups.ReceiverAdapter;
import org.jgroups.util.Queue;

/**
 * @author Lev
 *
 */
public class JGNode extends ReceiverAdapter implements IMiddleWareNode {

    JChannel channel = null;
    String nodeID = null;
    JGMsgHandler handler = new JGMsgHandler() {
        
    };
    ConcurrentLinkedQueue<JGMessage> buf = new ConcurrentLinkedQueue<JGMessage>();

    private Address id2address(String id) {
        for (Address a : channel.getView()) {
            if (a.toString().equals(id)) {
                return a;
            }
        }
        return null;
    }

    private String setNodeID() {
        return setNodeID(channel.getAddressAsString());
    }

    private String setNodeID(String id) {
        return nodeID = id;
    }

    public JGNode() throws Exception {
        this( new JChannel() );
    }

    JGNode(JChannel ch) {
        this.channel = ch;
        this.channel.setReceiver(this);
        setNodeID();
    }

    public JGNode(String id) {
        setNodeID(id);
    }

    @Override
    public List<IMiddleWareNode> getGroupListners() {
        List<IMiddleWareNode> res = new ArrayList<IMiddleWareNode>();
        for (Address a : channel.getView()) {
            res.add(new JGNode(a.toString()));
        }
        return res;
    }

    @Override
    public String getID() {
        System.out.println("nodeID:" + nodeID);
        if (nodeID != null) {
            return nodeID;
        }
        return setNodeID();
    }

    @Override
    public IMiddleWareGroup getJoinedGroup() {
        return new JGGroup(channel.getClusterName());
    }

    @Override
    public Object getUpdatedState() {
        throw new UnsupportedOperationException();
    }

    @Override
    public IMiddleWareNode joinGroup(IMiddleWareGroup group)
            throws JoinGroupException {
        try {
            this.channel.connect(group.getID());
        } catch (Exception ex) {
            Logger.getLogger(JGNode.class.getName()).log(Level.SEVERE, null, ex);
            throw new JoinGroupException(ex);
        }
        return this;
    }

    @Override
    public IMiddleWareNode leaveGroup()
            throws JoinGroupException {
        channel.close();
        return this;
    }

    @Override
    public IMiddlewareMessage receiveMsg() {
        /*
        final JGMessage res_msg = new JGMessage("");
        System.out.println("create: " + res_msg);
        channel.setReceiver(new ReceiverAdapter() {
            @Override
            public void receive(Message msg) {
                System.out.println("receive: " + msg);
                res_msg.setPayload(msg.getObject());
            }
        });
        System.out.println("return: " + res_msg);
        return res_msg;
        */
        /*
        if (this.currentMsg == null) {
            try {
                this.wait();
            } catch (InterruptedException ex) {
                Logger.getLogger(JGNode.class.getName()).log(Level.SEVERE, null, ex);
            }
        }
        JGMessage res_msg = new JGMessage(this.currentMsg);
        this.currentMsg = null;
        return res_msg;
        */
        return buf.poll();
    }

    @Override
    public void sendMsg(IMiddlewareMessage msg) {
        try {
            channel.send(new JGMessage(msg).get());
        } catch (Exception ex) {
            Logger.getLogger(JGNode.class.getName()).log(Level.SEVERE, null, ex);
        }
    }

    @Override
    public void sendMsgTo(IMiddlewareMessage msg, IMiddleWareNode receiver) {
        Address a = this.id2address(receiver.getID());
        System.out.println("a: " + a + "; isC" + this.channel.isConnected());
        Message mex = new Message(a, msg);
        try {
            channel.send(a , msg );
        } catch (Exception ex) {
            Logger.getLogger(JGNode.class.getName()).log(Level.SEVERE, null, ex);
        }
    }
    
    @Override
    public void receive(Message msg) {
        buf.add((JGMessage) msg.getObject());
        //System.out.println("receive msg = " + currentMsg);
        //notify();
    }

    public IMiddleWareMsgHandler setMsgHandler(IMiddleWareMsgHandler handler) {
        throw new UnsupportedOperationException("Not supported yet.");
    }

    public boolean hasMsg() {
        return !buf.isEmpty();
    }
    
}
